﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace RFEngine.Threading.Tasks
{
    public static class Parallel
    {
        #region Fields

#if WINDOWS
        private static int _NumberOfProcessors = Environment.ProcessorCount;

#elif XBOX
        /// This is the total amount of useable cores on the Xbox360
        /// http://msdn.microsoft.com/en-us/library/microsoft.xna.net_cf.system.threading.thread.setprocessoraffinity.aspx
        private static int _NumberOfProcessors = 4; 

#endif

        private static int _WorkThreads;
        private static int _NextWorkItem;

        #endregion

        /// <summary>
        /// A Method for running a For loop in parallel, running the interations in parallel.
        /// </summary>
        /// <param name="physicsThread">An integer value of the physicsThread index</param>
        /// <param name="end">An integer value of the end index</param>
        /// <param name="action">The action to perform each loop iteration</param>
        public static void For(int start, int end, Action<int> action)
        {
            _WorkThreads = _NumberOfProcessors;
            _NextWorkItem = start;

            if (end - start <= 0)
                return;

            if (end - start == 1)
            {
                action(1);
                return;
            }

            using (ManualResetEvent manualResetEvent = new ManualResetEvent(false))
            {
                for (int currentThread = 0; currentThread < _WorkThreads; currentThread++)
                {
                    ThreadPool.QueueUserWorkItem
                    (
                        delegate
                        {
                            int index;
                            while ((index = Interlocked.Increment(ref _NextWorkItem) - 1) < end)
                                action(index);

                            if (Interlocked.Decrement(ref _WorkThreads) <= 0)
                                manualResetEvent.Set();
                        }
                    );
                }

                manualResetEvent.WaitOne();
            }
        }

        /// <summary>
        /// A Method for running a Loop on a Collection in Parallel.
        /// </summary>
        /// <typeparam name="T">The Type of Collection to run in Parallel</typeparam>
        /// <param name="collection">The collection to run in parallel</param>
        /// <param name="action">The action to perform on each item of the collection</param>
        public static void ForEach<T>(IEnumerable<T> collection, Action<T> action)
        {
            var list = collection.ToArray();
            int start = 0;
            int end = list.Length;

            _WorkThreads = _NumberOfProcessors;
            _NextWorkItem = start;

            if (end - start <= 0)
                return;

            if (end - start == 1)
            {
                action(list.First());
                return;
            }

            using (ManualResetEvent manualResetEvent = new ManualResetEvent(false))
            {
                for (int currentThread = 0; currentThread < _WorkThreads; currentThread++)
                {
                    ThreadPool.QueueUserWorkItem
                    (
                        delegate
                        {
                            int index;
                            while ((index = Interlocked.Increment(ref _NextWorkItem) - 1) < end)
                                action(list[index]);

                            if (Interlocked.Decrement(ref _WorkThreads) <= 0)
                                manualResetEvent.Set();
                        }
                    );
                }

                manualResetEvent.WaitOne();
            }
        }
    }
}
